// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef ASH_UTILITY_SCREENSHOT_CONTROLLER_H_
#define ASH_UTILITY_SCREENSHOT_CONTROLLER_H_

#include <stdint.h>

#include <map>
#include <memory>

#include "ash/ash_export.h"
#include "ash/shell_observer.h"
#include "base/callback.h"
#include "base/macros.h"
#include "base/timer/timer.h"
#include "ui/aura/window_observer.h"
#include "ui/display/display_observer.h"
#include "ui/events/event_handler.h"
#include "ui/gfx/geometry/point.h"

namespace aura {
class Window;
}

namespace ui {
class LocatedEvent;
struct PointerDetails;
}

namespace ash {
class ScreenshotDelegate;

// This class controls a session of taking partial/window screenshot, i.e.:
// drawing region rectangles during selection, and changing the mouse cursor to
// indicate the current mode.
class ASH_EXPORT ScreenshotController : public ui::EventHandler,
                                        public display::DisplayObserver,
                                        public aura::WindowObserver {
 public:
  explicit ScreenshotController(std::unique_ptr<ScreenshotDelegate> delegate);
  ~ScreenshotController() override;

  // Takes a default "whole screen" screenshot.
  void TakeScreenshotForAllRootWindows();

  // Starts the UI for taking partial screenshot; dragging to select a region.
  // ScreenshotController manage their own lifetime so caller must not
  // delete the returned values. |draw_overlay_immediately| controls if the grey
  // overlay will be drawn immediately. If false, then the overlay will be drawn
  // only after the user has started creating the clipping rect for the
  // screenshot.
  void StartPartialScreenshotSession(bool draw_overlay_immediately);

  // Starts the UI for taking a window screenshot;
  void StartWindowScreenshotSession();

  // Cancels any active screenshot session.
  void CancelScreenshotSession();

  // Set a function that will be called when the current screenshot session has
  // been completed or cancelled. This is reset after the screenshot session is
  // done.
  void set_on_screenshot_session_done(base::OnceClosure on_done) {
    on_screenshot_session_done_ = std::move(on_done);
  }

  // If set to true, then only events generated by a pen can be used to select
  // the area to take a screenshot of. This is reset to false after a screenshot
  // operation is completed.
  void set_pen_events_only(bool pen_events_only) {
    pen_events_only_ = pen_events_only;
  }
  bool pen_events_only() const { return pen_events_only_; }

 private:
  enum Mode {
    NONE,
    PARTIAL,
    WINDOW,
  };

  friend class AshTestBase;
  friend class ScreenshotControllerTest;
  friend class ScreenshotToolTest;

  class ScopedCursorSetter;
  class ScreenshotLayer;

  // Starts, ends, cancels, or updates the region selection.
  void CompleteWindowScreenshot();
  void CompletePartialScreenshot();
  void Update(const ui::LocatedEvent& event);
  void UpdateSelectedWindow(const ui::LocatedEvent& event);
  void SetSelectedWindow(aura::Window* window);

  // Returns true if the event should be processed.
  bool ShouldProcessEvent(const ui::PointerDetails& pointer_details) const;

  // ui::EventHandler:
  void OnKeyEvent(ui::KeyEvent* event) override;
  void OnMouseEvent(ui::MouseEvent* event) override;
  void OnTouchEvent(ui::TouchEvent* event) override;

  // display::DisplayObserver:
  void OnDisplayAdded(const display::Display& new_display) override;
  void OnDisplayRemoved(const display::Display& old_display) override;
  void OnDisplayMetricsChanged(const display::Display& display,
                               uint32_t changed_metrics) override;

  // aura::WindowObserver:
  void OnWindowDestroying(aura::Window* window) override;

  // Records the number of screenshots taken.
  void RecordNumberOfScreenshotsTakenInLastDay();
  void RecordNumberOfScreenshotsTakenInLastWeek();

  gfx::Point GetStartPositionForTest() const;

  Mode mode_;

  // If true, then only pointer events will be used when selecting the
  // screenshot region.
  bool pen_events_only_ = false;

  base::OnceClosure on_screenshot_session_done_;

  // The data to build the screenshot region.
  aura::Window* root_window_;

  // Currently selected window in WINDOW mode.
  aura::Window* selected_;

  // Layers to create the visual effect of region selection or selected window.
  std::map<aura::Window*, std::unique_ptr<ScreenshotLayer>> layers_;

  // The object to specify the crosshair cursor.
  std::unique_ptr<ScopedCursorSetter> cursor_setter_;

  // True while taking a partial or window screen.
  bool in_screenshot_session_ = false;

  // TODO(jamescook): Replace with a mojo-compatible interface.
  std::unique_ptr<ScreenshotDelegate> screenshot_delegate_;

  // Timers used to schedule recording of the number of screenshots taken.
  base::RepeatingTimer num_screenshots_taken_in_last_day_scheduler_;
  base::RepeatingTimer num_screenshots_taken_in_last_week_scheduler_;

  // Counters used to track the number of screenshots taken.
  int num_screenshots_taken_in_last_day_ = 0;
  int num_screenshots_taken_in_last_week_ = 0;

  base::WeakPtrFactory<ScreenshotController> weak_factory_{this};

  DISALLOW_COPY_AND_ASSIGN(ScreenshotController);
};

}  // namespace ash

#endif  // ASH_UTILITY_SCREENSHOT_CONTROLLER_H_
